"""
Subtitle generator for creating SRT subtitle files.

Generates subtitle files synchronized with audio narration.
"""

import re
from typing import List, Dict, Optional, Tuple
from dataclasses import dataclass
from datetime import timedelta

from ..script.script_generator import PresentationScript, ScriptSegment
from ..utils.exceptions import MD2PPTError


@dataclass
class SubtitleEntry:
    """A single subtitle entry."""
    index: int
    start_time: timedelta
    end_time: timedelta
    text: str
    
    def to_srt_format(self) -> str:
        """Convert to SRT format string."""
        start_str = self._timedelta_to_srt_time(self.start_time)
        end_str = self._timedelta_to_srt_time(self.end_time)
        
        return f"{self.index}\n{start_str} --> {end_str}\n{self.text}\n"
    
    def _timedelta_to_srt_time(self, td: timedelta) -> str:
        """Convert timedelta to SRT time format (HH:MM:SS,mmm)."""
        total_seconds = int(td.total_seconds())
        milliseconds = int(td.microseconds / 1000)
        
        hours = total_seconds // 3600
        minutes = (total_seconds % 3600) // 60
        seconds = total_seconds % 60
        
        return f"{hours:02d}:{minutes:02d}:{seconds:02d},{milliseconds:03d}"


@dataclass
class SubtitleConfig:
    """Configuration for subtitle generation."""
    max_chars_per_line: int = 42  # Maximum characters per line
    max_lines_per_subtitle: int = 2  # Maximum lines per subtitle
    min_duration: float = 1.0  # Minimum subtitle duration in seconds
    max_duration: float = 7.0  # Maximum subtitle duration in seconds
    words_per_minute: float = 150  # Reading speed for timing
    break_on_punctuation: bool = True  # Break on sentence endings
    language: str = "zh-CN"
    
    def get_reading_speed(self) -> float:
        """Get reading speed in characters/words per second."""
        if self.language.startswith("zh"):
            # Chinese: characters per second
            return (self.words_per_minute * 2) / 60  # ~2 chars per "word"
        else:
            # English: words per second
            return self.words_per_minute / 60


class SubtitleGenerator:
    """Generates SRT subtitle files from presentation scripts."""
    
    def __init__(self, config: Optional[SubtitleConfig] = None):
        """Initialize subtitle generator."""
        self.config = config or SubtitleConfig()
    
    def generate_subtitles(self, script: PresentationScript,
                         audio_timings: Optional[Dict[int, Tuple[float, float]]] = None) -> List[SubtitleEntry]:
        """
        Generate subtitles from presentation script.
        
        Args:
            script: Presentation script
            audio_timings: Optional mapping of slide number to (start_time, duration)
            
        Returns:
            List of subtitle entries
        """
        subtitles = []
        current_time = 0.0
        subtitle_index = 1
        
        for segment in script.segments:
            if audio_timings and segment.slide_number in audio_timings:
                # Use provided timing
                start_time, duration = audio_timings[segment.slide_number]
                current_time = start_time
                segment_duration = duration
            else:
                # Use estimated timing
                segment_duration = segment.duration_estimate
            
            # Generate subtitles for this segment
            segment_subtitles = self._generate_segment_subtitles(
                segment.content, 
                current_time, 
                segment_duration,
                subtitle_index
            )
            
            subtitles.extend(segment_subtitles)
            subtitle_index += len(segment_subtitles)
            current_time += segment_duration
        
        return subtitles
    
    def save_srt_file(self, subtitles: List[SubtitleEntry], output_path: str) -> None:
        """Save subtitles to SRT file."""
        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                for subtitle in subtitles:
                    f.write(subtitle.to_srt_format())
                    f.write('\n')  # Empty line between entries
        except Exception as e:
            raise SubtitleGenerationError(f"Failed to save SRT file: {e}")
    
    def _generate_segment_subtitles(self, text: str, 
                                  start_time: float, 
                                  duration: float,
                                  start_index: int) -> List[SubtitleEntry]:
        """Generate subtitles for a single segment."""
        # Split text into subtitle chunks
        chunks = self._split_text_into_chunks(text)
        
        if not chunks:
            return []
        
        subtitles = []
        chunk_duration = duration / len(chunks)
        
        # Ensure minimum duration
        chunk_duration = max(chunk_duration, self.config.min_duration)
        
        current_time = start_time
        
        for i, chunk in enumerate(chunks):
            # Calculate timing for this chunk
            subtitle_start = timedelta(seconds=current_time)
            
            # Adjust duration based on text length
            text_duration = self._calculate_text_duration(chunk)
            actual_duration = min(max(text_duration, self.config.min_duration), 
                                self.config.max_duration)
            
            subtitle_end = timedelta(seconds=current_time + actual_duration)
            
            subtitle = SubtitleEntry(
                index=start_index + i,
                start_time=subtitle_start,
                end_time=subtitle_end,
                text=chunk.strip()
            )
            
            subtitles.append(subtitle)
            current_time += actual_duration
        
        return subtitles
    
    def _split_text_into_chunks(self, text: str) -> List[str]:
        """Split text into appropriately sized subtitle chunks."""
        # Remove extra whitespace
        text = re.sub(r'\s+', ' ', text.strip())
        
        if not text:
            return []
        
        chunks = []
        
        if self.config.language.startswith("zh"):
            # Chinese text processing
            chunks = self._split_chinese_text(text)
        else:
            # English text processing
            chunks = self._split_english_text(text)
        
        return [chunk for chunk in chunks if chunk.strip()]
    
    def _split_chinese_text(self, text: str) -> List[str]:
        """Split Chinese text into subtitle chunks."""
        # Split on major punctuation first
        sentences = re.split(r'[。！？；]', text)
        chunks = []
        
        for sentence in sentences:
            sentence = sentence.strip()
            if not sentence:
                continue
            
            # If sentence is short enough, use as is
            if len(sentence) <= self.config.max_chars_per_line:
                chunks.append(sentence)
                continue
            
            # Split longer sentences
            # Try to split on commas, periods, etc.
            sub_parts = re.split(r'[，、：]', sentence)
            
            current_chunk = ""
            for part in sub_parts:
                part = part.strip()
                if not part:
                    continue
                
                # Check if adding this part would exceed limit
                test_chunk = current_chunk + ("，" if current_chunk else "") + part
                
                if len(test_chunk) <= self.config.max_chars_per_line:
                    current_chunk = test_chunk
                else:
                    # Save current chunk and start new one
                    if current_chunk:
                        chunks.append(current_chunk)
                    current_chunk = part
            
            # Don't forget the last chunk
            if current_chunk:
                chunks.append(current_chunk)
        
        return chunks
    
    def _split_english_text(self, text: str) -> List[str]:
        """Split English text into subtitle chunks."""
        # Split into sentences first
        sentences = re.split(r'[.!?;]', text)
        chunks = []
        
        for sentence in sentences:
            sentence = sentence.strip()
            if not sentence:
                continue
            
            words = sentence.split()
            
            # If sentence fits in one line
            if len(' '.join(words)) <= self.config.max_chars_per_line:
                chunks.append(' '.join(words))
                continue
            
            # Split into multiple lines
            current_chunk = ""
            
            for word in words:
                test_chunk = current_chunk + (" " if current_chunk else "") + word
                
                if len(test_chunk) <= self.config.max_chars_per_line:
                    current_chunk = test_chunk
                else:
                    # Save current chunk and start new one
                    if current_chunk:
                        chunks.append(current_chunk)
                    current_chunk = word
            
            # Don't forget the last chunk
            if current_chunk:
                chunks.append(current_chunk)
        
        return chunks
    
    def _calculate_text_duration(self, text: str) -> float:
        """Calculate duration needed to read text."""
        if self.config.language.startswith("zh"):
            # Chinese: character-based
            char_count = len(text)
            return char_count / self.config.get_reading_speed()
        else:
            # English: word-based
            word_count = len(text.split())
            return word_count / self.config.get_reading_speed()
    
    def generate_segment_timing_map(self, script: PresentationScript) -> Dict[int, Tuple[float, float]]:
        """
        Generate timing map for script segments.
        
        Returns:
            Dictionary mapping slide number to (start_time, duration)
        """
        timing_map = {}
        current_time = 0.0
        
        for segment in script.segments:
            timing_map[segment.slide_number] = (current_time, segment.duration_estimate)
            current_time += segment.duration_estimate
        
        return timing_map
    
    def adjust_subtitles_for_audio(self, subtitles: List[SubtitleEntry],
                                 audio_segments: List[Tuple[float, float]]) -> List[SubtitleEntry]:
        """
        Adjust subtitle timing to match actual audio segments.
        
        Args:
            subtitles: Original subtitles
            audio_segments: List of (start_time, duration) for each audio segment
            
        Returns:
            Adjusted subtitles
        """
        if len(audio_segments) != len(subtitles):
            # If counts don't match, distribute evenly
            adjusted_subtitles = []
            
            for i, subtitle in enumerate(subtitles):
                if i < len(audio_segments):
                    start_time, duration = audio_segments[i]
                    
                    adjusted_subtitle = SubtitleEntry(
                        index=subtitle.index,
                        start_time=timedelta(seconds=start_time),
                        end_time=timedelta(seconds=start_time + duration),
                        text=subtitle.text
                    )
                    adjusted_subtitles.append(adjusted_subtitle)
                else:
                    # Use original timing for extra subtitles
                    adjusted_subtitles.append(subtitle)
            
            return adjusted_subtitles
        else:
            # Direct mapping
            adjusted_subtitles = []
            
            for subtitle, (start_time, duration) in zip(subtitles, audio_segments):
                adjusted_subtitle = SubtitleEntry(
                    index=subtitle.index,
                    start_time=timedelta(seconds=start_time),
                    end_time=timedelta(seconds=start_time + duration),
                    text=subtitle.text
                )
                adjusted_subtitles.append(adjusted_subtitle)
            
            return adjusted_subtitles


class SubtitleGenerationError(MD2PPTError):
    """Raised when subtitle generation fails."""
    pass